if (typeof(TRAVIAN_UTILS) != 'boolean') {
	var TRAVIAN_UTILS = true;

	var TrvnUtils = {};

	TrvnUtils.Logger = {
		info : function(msg) {
			if (TrvnCfg.enableLog) {
				try {
					Travian.toolDoc.addListPaneItem("logger", Travian.toolDoc.createTextNode("INFO: " + msg));
				} catch (e) {
					alert(msg);
				}
			}
		},
		warn : function(msg) {
			if (TrvnCfg.enableLog) {
				try {
					var txt = Travian.toolDoc.$C("font");
					txt.style.color = "Yellow";
					txt.appendChild(Travian.toolDoc.createTextNode("WARN: " + msg));
					Travian.toolDoc.addListPaneItem("logger", txt);
				} catch (e) {
					alert(msg);
				}
			}
		},
		error : function(msg) {
			if (TrvnCfg.enableLog) {
				try {
					var txt = Travian.toolDoc.$C("font");
					txt.style.color = "Red";
					txt.appendChild(Travian.toolDoc.createTextNode("ERROR: " + msg));
					Travian.toolDoc.addListPaneItem("logger", txt);
				} catch (e) {
					alert(msg);
				}
			}
		},
		clear : function() {
			Travian.toolDoc.$G("logger").innerHTML = "";
		},
		create : function() {
			return Travian.toolDoc.createListPane("logger", "调试信息", true);
		}
		,
	};

	TrvnUtils.$Doc = function(iframeObj) {
		var doc = iframeObj.contentWindow.document;
		TrvnUtils.initDoc(doc);
		return doc;
	}
	TrvnUtils.parseNumber = function(s) {
		if ((typeof s) == "number") return s;
		s = s.toString().replace(/^([+-]?)0*(\d*).*$/, "$1$2");
		if (s == "" || s == "-" || s == "+") return 0;
		else return parseInt(s);
	}
	TrvnUtils.each = function(obj, iterator) {
		if (!obj || !iterator) return;
		var index = 0;
		for (var key in obj) {
			if ((typeof obj[key]) != "function" && key != '$family') {
				iterator(key, obj[key], index++);
			}
		}
	}
	TrvnUtils.timeToMilliseconds = function(s) {
		var ts = s.split(":");
		var senconds = 0;
		var u = 1;
		for (var i = ts.length - 1;i >= 0; i--) {
			senconds += TrvnUtils.parseNumber(ts[i]) * u;
			u *= 60;
		}
		return senconds * 1000;
	}
	TrvnUtils.millisecondsToTime = function(ms) {
		if (NaN == parseInt(ms)) return ms;
		var mms = parseInt(parseInt(ms) / 1000);
		var ts = mms % 60;
		ts = ts < 10 ? "0" + ts : ts;
		if (mms / 60 > 0) {
			var tt = (parseInt(mms / 60)) % 60;
			tt = tt < 10 ? "0" + tt : tt;
			ts = tt + ":" + ts;
		}
		if (mms / 3600 > 0) {
			ts = parseInt(mms / 3600) + ":" + ts;
		}
		return ts;
	}
	TrvnUtils.evaluateXPath = function(aNode, aExpr) {
		var xpe = new XPathEvaluator();
		var nsResolver = xpe.createNSResolver(aNode.ownerDocument == null
				? aNode.documentElement
				: aNode.ownerDocument.documentElement);
		var result = xpe.evaluate(aExpr, aNode, nsResolver, 0, null);
		var found = [];
		var res;
		while (res = result.iterateNext())
			found.push(res);
		return found;
	}
	TrvnUtils.promptLink = function(defaultLink) {
		var sLink = prompt("格式：\n链接名=http://www.abc.com/", defaultLink.title + "=" + defaultLink.link);
		if (sLink) {
			var splitIndex = sLink.indexOf("=");
			if (splitIndex <= 0) return null;
			return {
				title : sLink.substring(0, splitIndex),
				link : sLink.substring(splitIndex + 1, sLink.length)
			};
		}
		return null;
	}
	TrvnUtils.initDoc = function(doc) {
		doc.evaluateXPath = function(aExpr) {
			return TrvnUtils.evaluateXPath(this, aExpr);
		}
		doc.$G = function(id) {
			return this.getElementById(id);
		}
		doc.$C = function(t) {
			return this.createElement(t);
		}
		doc.createA = function(href, html, target) {
			var a = this.$C("a");
			a.href = href;
			a.innerHTML = html;
			// a.target = target ? target : "travian";
			return a;
		}
		doc.createPane = function(title, contentId) {
			var div = this.$C("div");
			div.id = "pane_" + contentId;
			div.className = "pane " + TrvnCfg.toolPanePosition;
			var captionDiv = this.$C("div");
			captionDiv.className = "rbg title";
			var titleSpan = this.$C("span");
			titleSpan.innerHTML = title;
			captionDiv.appendChild(titleSpan);
			div.appendChild(captionDiv);
			var contentDiv = this.$C("div");
			div.appendChild(contentDiv);
			contentDiv.id = contentId;
			contentDiv.className = "body";
			div.contentDiv = contentDiv;
			return div;
		}
		doc.parseUrl = function(sUrl, callback) {
			var parseIFrame = this.$C("iframe");
			parseIFrame.name = "parseIFrame";
			parseIFrame.id = "parseIFrame";
			parseIFrame.style.display = "none";
			this.body.appendChild(parseIFrame);
			var thisObject = this;
			parseIFrame.addEventListener('load', function() {
				try {
					callback.doParse(TrvnUtils.$Doc(this), this.contentWindow);
					setTimeout(function() {
						thisObject.body.removeChild(parseIFrame);
					}, 100);
				} catch (e) {
					TrvnUtils.Logger.error("parseUrl[" + sUrl + "]:" + e)
				}
			}, true);
			parseIFrame.contentDocument.location.href = sUrl;
		}
		doc.addListPaneItem = function(id, domObj) {
			var li = this.$C("li");
			li.appendChild(domObj);
			this.$G(id).appendChild(li);
			li.scrollIntoView(false);
		}
		doc.createListPane = function(id, title, canClear) {
			var thisDoc = this;
			var pane = thisDoc.createPane(title, id);
			if (canClear) {
				var button = thisDoc.$C("button");
				button.type = "button";
				button.addEventListener('click', function() {
					thisDoc.$G(id).innerHTML = "";
				}, false);
				button.innerHTML = "清空";
				pane.appendChild(button);
			}
			return pane;
		}
	}
	var XMLUtils = {};
	XMLUtils.XMLString = '<?xml version="1.0" encoding="UTF-8"?>';
	XMLUtils.NewLine = '\n';
	XMLUtils.Entry = function(k, v) {
		var thisObject = this;
		this.key = k;
		this.value = v;
		this.toString = function() {
			return "{" + thisObject.key + "=" + thisObject.value + "}";
		}
	}
	XMLUtils.EntryObject = function() {
		var thisObject = this;
		this.toString = function() {
			var s = "{\n";
			for (var k in thisObject) {
				if ((typeof thisObject[k]) != "function") {
					s += k + "=" + thisObject[k] + "\n";
				}
			}
			return s + "}";
		}
	}
	XMLUtils.escapeXML = function(s) {
		if (!s) return s;
		return s.replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;').replace(/"/g, '&quot;').replace(/'/g, '&apos;');
	}
	XMLUtils.transformObject = function(obj, depth) {
		var xmlString = '';
		var indentString = '';
		for (var i = 0;i < depth; i++) {
			indentString += '    ';
		}
		for (var k in obj) {
			var type = (typeof obj[k]);
			k = XMLUtils.escapeXML(k);
			var value = obj[k];
			switch (type) {
				case "string" :
					value = XMLUtils.escapeXML(value);
				case "boolean" :
				case "number" : {
					xmlString += indentString + '<' + type + ' name="' + k + '">' + value + '</' + type + '>'
							+ XMLUtils.NewLine;
					break;
				}
				case "object" : {
					var objectString = XMLUtils.transformObject(obj[k], depth + 1);
					if (!obj[k]) {
						xmlString += indentString + '<null name="' + k + '"/>' + XMLUtils.NewLine;
					} else if (obj[k] instanceof Array) {
						xmlString += indentString + '<array name="' + k + '">' + XMLUtils.NewLine + objectString
								+ indentString + '</array>' + XMLUtils.NewLine;
					} else {
						xmlString += indentString + '<object name="' + k + '">' + XMLUtils.NewLine + objectString
								+ indentString + '</object>' + XMLUtils.NewLine;
					}
					break;
				}
			}
		}
		return xmlString;
	}
	XMLUtils.objectToXMLString = function(obj) {
		var xmlString = XMLUtils.XMLString + XMLUtils.NewLine;
		xmlString += '<object>' + XMLUtils.NewLine;
		xmlString += XMLUtils.transformObject(obj, 1);
		xmlString += '</object>' + XMLUtils.NewLine;
		return xmlString;
	}
	XMLUtils.domToObject = function(tag) {
		if (!tag || !tag.tagName) {
			return null;
		}
		var entry = new XMLUtils.Entry();
		entry.key = tag.getAttribute("name");
		switch (tag.tagName) {
			case "null" : {
				entry.value = null;
				break;
			}
			case "string" : {
				entry.value = tag.getAttribute("value");
				if (!entry.value && tag.firstChild) {
					entry.value = tag.firstChild.nodeValue;
				}
				break;
			}
			case "number" : {
				entry.value = tag.getAttribute("value");
				if (!entry.value && tag.firstChild) {
					entry.value = parseInt(tag.firstChild.nodeValue);
				}
				break;
			}
			case "boolean" : {
				entry.value = tag.getAttribute("value");
				if (!entry.value && tag.firstChild) {
					entry.value = tag.firstChild.nodeValue;
				}
				entry.value = entry.value ? entry.value == "true" : false;
				break;
			}
			case "object" : {
				entry.value = new XMLUtils.EntryObject();
				for (var i = 0;i < tag.childNodes.length; i++) {
					var newEntry = XMLUtils.domToObject(tag.childNodes.item(i));
					if (newEntry) {
						entry.value[newEntry.key] = newEntry.value;
					}
				}
				break;
			}
			case "array" : {
				entry.value = new Array();
				for (var i = 0;i < tag.childNodes.length; i++) {
					var newEntry = XMLUtils.domToObject(tag.childNodes.item(i));
					if (newEntry) {
						entry.value[newEntry.key] = newEntry.value;
					}
				}
				break;
			}
			default :
				throw "未知的标记 \"" + tag.tagName + "\".";
				break;
		}
		return entry;
	}
	XMLUtils.readFromFile = function(filePath) {
		var fileIn = FileIO.open(filePath);
		var configDOM;
		if (fileIn.exists()) {
			var parser = new DOMParser();
			configDOM = parser.parseFromString(FileIO.read(fileIn, "UTF-8"), "text/xml").documentElement;
		} else {
			throw "文件 \"" + filePath + "\" 不存在.";
		}
		if (configDOM.tagName != "object") throw "配置文件必须以object为根结点!";
		return XMLUtils.domToObject(configDOM).value;
	}
	XMLUtils.writeIntoFile = function(obj, filePath) {
		try {
			var xmlString = XMLUtils.objectToXMLString(obj);
			var fileOut = FileIO.open(filePath);
			FileIO.write(fileOut, xmlString, 'w', 'UTF-8');
		} catch (e) {
			TrvnUtils.Logger.error("config.js 401: " + e);
		}
	}

	var TrvnStyleUtil = {
		getAbsoluteLeft : function(obj, tagName) {
			var offset = 0;
			while (obj) {
				if (tagName && obj.tagName == tagName) break;
				offset += obj.offsetLeft;
				obj = obj.offsetParent;
			}
			return offset;
		},
		getAbsoluteTop : function(obj, tagName) {
			var offset = 0;
			while (obj) {
				if (tagName && obj.tagName == tagName) break;
				offset += obj.offsetTop;
				obj = obj.offsetParent;
			}
			return offset;
		},
		getLengthAsNumber : function(l) {
			l = l.trim();
			if (l.charAt(0) == '-') return -new Number(l.replace(/\D/g, ""));
			return new Number(l.replace(/\D/g, ""));
		},
		FLOAT_BOTTOM : 0x01,
		FLOAT_TOP : 0x02,
		FLOAT_LEFT : 0x04,
		FLOAT_RIGHT : 0x08,
		ADJUST_LEFT : 0x10,
		ADJUST_RIGHT : 0x20,
		ADJUST_TOP : 0x40,
		ADJUST_BOTTOM : 0x80,
		floatOn : function(srcObj, destObj, fp, ox, oy) {
			if (!srcObj || !destObj) {
				return;
			}
			var normalize = function(v) {
				return v >= 0 ? v : 0;
			}
			fp = fp ? fp : this.FLOAT_BOTTOM | this.ADJUST_LEFT;
			if (fp & (this.FLOAT_BOTTOM | this.FLOAT_TOP)) {
				srcObj.style.top = normalize((fp & this.FLOAT_TOP)
						? (this.getAbsoluteTop(destObj) - srcObj.offsetHeight)
						: (this.getAbsoluteTop(destObj) + destObj.offsetHeight))
						+ "px";
				srcObj.style.left = normalize((fp & this.ADJUST_RIGHT) ? (this.getAbsoluteLeft(destObj)
						+ destObj.offsetWidth - srcObj.offsetWidth) : (this.getAbsoluteLeft(destObj)))
						+ "px";
			} else {
				srcObj.style.left = normalize((fp & this.FLOAT_LEFT)
						? (this.getAbsoluteLeft(destObj) - srcObj.offsetWidth)
						: (this.getAbsoluteLeft(destObj) + destObj.offsetWidth))
						+ "px";
				srcObj.style.top = normalize((fp & this.ADJUST_BOTTOM) ? (this.getAbsoluteTop(destObj)
						+ destObj.offsetHeight - srcObj.offsetHeight) : (this.getAbsoluteTop(destObj)))
						+ "px";
			}
			srcObj.style.top = normalize(this.getLengthAsNumber(srcObj.style.top) + (oy ? oy : 0)) + "px";
			srcObj.style.left = normalize(this.getLengthAsNumber(srcObj.style.left) + (ox ? ox : 0)) + "px";
		}
	};

}